#pragma once
#include "Socket.hpp"
#include "Log.hpp"
#include <fstream>
#include <string>
#include <sstream>
#include <vector>

#include<unordered_map>
static const int defaultport = 8082;
const std::string sep = "\r\n";
const std::string wwwroot = "./wwwroot";   // web 根目录
const std::string homepage = "index.html"; // 首页

class HttpServer;
class ThreadData
{
public:
    ThreadData(int fd,  HttpServer *s) : sockfd(fd) ,svr(s)
    {
    }

public:
    int sockfd;
     HttpServer *svr;
};

class HttpRequest
{
public:
    void Deserialize(std::string req)
    {
        // 用空格将req打散 ,提取报头和正文（有效载荷）
        while (true)
        {
            // 提取报头
            size_t pos = req.find(sep);
            if (pos == std::string::npos)
                break;
            std::string temp = req.substr(0, pos);
            if (temp.empty())
                break;
            req_header.push_back(temp);
            req.erase(0, pos + sep.size());
        }
        // 提取正文
        text = req;
    }
    void Parse() // 解析请求行 ，将 Method URL HTTP Version 解析出来
    {
        std::stringstream ss(req_header[0]);
        //std::cout << "req_header[0]: " << req_header[0] << std::endl;
        ss >> method >> url >> http_version; // method、url、http_version顺序不能颠倒

        file_path = wwwroot;
        if (url == "/" || url == "/index.html")
        {
            file_path += "/";
            file_path += homepage; // ./wwwroot/index.html
        }
        else                  // 不是/ ,是/a/b/c.html的这种常规路径
            file_path += url; // /a/b/c/d.html->./wwwroot/a/b/c/d.html
        //从后往前找,通过后缀的不同来判断
        auto pos = file_path.rfind("." ) ;
        if(pos ==std::string::npos)  suffix = ".html"; //没找到
        //找到了
        else suffix = file_path.substr(pos);
    }
    void DebugPrint()
    {
        for (auto &line : req_header)
        {
            std::cout << "--------------------------------" << std::endl;
            std::cout << line << "\n\n";
        }

        std::cout << "method: " << method << std::endl;
        std::cout << "url: " << url << std::endl;
        std::cout << "http_version: " << http_version << std::endl;
        std::cout << "file_path: " << file_path << std::endl;
        std::cout << text << std::endl;
    }

public:
    std::vector<std::string> req_header; // 请求报头
    std::string text;                    // 请求正文

    // 解析之后的结果
    std::string method;
    std::string url;
    std::string http_version;
    std::string file_path; // ./wwwroot/a/b/c.html 2.png

     std::string suffix; //后缀
};

class HttpServer
{
public:
    HttpServer(uint16_t port = defaultport) : port_(port)
    {
          content_type.insert({".html", "text/html"});
        content_type.insert({".png", "image/png"});
    }
    ~HttpServer()
    {
    }
  
public:
    static std::string ReadHtmlContent(const std::string &htmlpath) // 打开htmlpath文件
    {
        //以二进制方式来读文件
        std::ifstream in(htmlpath,std::ios::binary);
        // 打开文件
        if (!in.is_open())
            return " ";
            
        in.seekg(0, std::ios_base::end);
        auto len = in.tellg();
        in.seekg(0, std::ios_base::beg);

        std::string content;
        content.resize(len);

        in.read((char*)content.c_str(), content.size());


        // std::string content;
        // std::string line;
        // while (getline(in, line))
        // {
        //     content += line;
        // }
        in.close();
        return content;
    }
   

  
  
     void HandlerHttp(int sockfd)
    {
        char buffer[10240];
        // 接受请求
        ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); // 与read函数类似
        if (n > 0)                                               // 接受成功
        {
            buffer[n] = 0;                    // 将buffer当成字符串处理
            std::cout << buffer << std::endl; // 假设我们读取到的就是一个完整的，独立的http 请求
            HttpRequest req;
            // 将Http请求反序列化
            req.Deserialize(buffer);
            req.Parse();
            // lg(Info, "DebugPrint sucess");

            // req.DebugPrint() ;

            // std::string url = "/a/a/b";
            // std::string path = wwwroot;
            // path += url; //  ./wwwroot/a/a/b

            // 构建响应

            std::string text;
            text = ReadHtmlContent(req.file_path);
            // 打开文件失败
            bool ok = true; // 标致位

            // if(text.empty())
            // {
            //     ok =false ;
            //         std::string err_html  ;
            //         err_html= wwwroot ;
            //         err_html+="/" ;
            //         err_html += "err.html"; // ./wwwroot/err.html
            //         text= ReadHtmlContent(err_html );
            //         lg(Info , "./wwwroot/err.html sucess");
            // }

            if (text.empty())
            {
                ok = false;
                std::string err_html = wwwroot;
                err_html += "/";
                err_html += "err.html";
                text = ReadHtmlContent(err_html);
            }

            std::string response_line;
            if (ok)
            { 
                response_line = "HTTP/1.0 200 OK\r\n";
            }
            else
            {
                response_line = "HTTP/1.0 404 Not Found\r\n";
            }
                  
            std::string response_header = "Content-Length:";
            response_header += std::to_string(text.size()); // Content-Length:11
            response_header += "\r\n";
                //根据后缀来添加对应
               response_header += "Content-Type: ";
            
            response_header += this->SuffixToDesc(req.suffix); 
             response_header += "\r\n";
            std::string blank_line = "\r\n"; // 空行，表示头部和正文的分隔
            std::string response = response_line;
            response += response_header;
            response += blank_line;
            response += text;
            // 返回响应
            send(sockfd, response.c_str(), response.size(), 0); // send函数类似于write
        }
        close(sockfd);
    }
    // 不加static ,ThreadRun函数是类的成员函数 ，void *ThreadRun(HttpServer * this ,void *args)
    // 与pthread_create的参数  ，void *(*start_routine) (void *) ,参数个数不一致
    // 加了static，ThreadRun就不是类的成员函数
    static void *ThreadRun(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = (ThreadData *)args;
        td->svr->HandlerHttp(td->sockfd);
        close(td->sockfd);
        delete td;
        return nullptr;
    }
       //根据文件后缀转成对应的描述

     std::string SuffixToDesc(const std::string &suffix)
    {
          auto iter = content_type.find(suffix);
          //没找到
          if(iter == content_type.end()) return content_type[".html"];
          else return content_type[suffix];

    }
   
    bool Start()
    {
        // 创建套接字
        listensock_.Socket();
        // 绑定端口
        listensock_.Bind(port_);
        // 监听
        listensock_.Lisenten();
        // 服务
        for (;;)
        {
            // 服务器需要获取到客户端的连接请求
            std::string clientip;
            uint16_t clientport;
            int sockfd = listensock_.Accept(&clientip, &clientport);
            if (sockfd < 0)
                continue;
            lg(Info, " HttpServer.cc:get a new connect, sockfd: %d", sockfd);
            // 线程
            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd,this);
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }

private:
    Sock listensock_; // 监听套接字
    uint16_t port_;
       std::unordered_map<std::string, std::string> content_type;//content_type表
};

